package tianhao.luo.sparsearr;

import java.io.*;

/**
 * 稀疏数组
 *
 * @author: tianhao.luo@hand-china.com 2021/6/13  9:44
 */
public class SparseArray {
    /**
     * 扩容数组的因子
     */
    private static final int BASE_INT = 2;

    /**
     * 稀疏数组行大小
     */
    private int baseSparseRowSize = 6;
    /**
     * 稀疏数组默认的列大小为3
     */
    private static final int BASE_SPARSE_COLUMN_SIZE = 3;

    /**
     * 默认的无效值
     */
    private static final int INVALID_VALUE = 0;

    /**
     * 原始二维数组
     */
    public int[][] originalArray;
    /**
     * 稀疏数组
     */
    public int[][] sparseArray;

    /**
     * 持久化文件名称
     */
    private static final String ENDURANCE_FILE_NAME = "map.data";

    public SparseArray(int[][] array, int baseSparseRowSize, boolean isSparseArray) {
        this.baseSparseRowSize = baseSparseRowSize;
        if (isSparseArray) {
            this.sparseArray = array;
            this.originalArray = sparseArray2OriginalArray();
        } else {
            this.originalArray = array;
            this.sparseArray = originalArray2SparseArray();
        }
    }


    /**
     * 打印二维数组
     *
     * @param array 二维数组
     */
    public void printArray(int[][] array) {
        // 打印二维数组
        for (int[] ints : array) {
            for (int anInt : ints) {
                System.out.printf("%d\t", anInt);

            }
            System.out.println();
        }
    }

    /**
     * 扩容数组,并拷贝原数据
     *
     * @param array 二维数组
     * @return 扩容后的数组
     */
    public int[][] extendArray(int[][] array, boolean isExtendRow, boolean isExtendColumn) {
        int[][] extendArray = new int[isExtendRow ? array.length * BASE_INT : array.length][isExtendColumn ? array[0].length * BASE_INT : array[0].length];
        for (int x = 0; x < array.length; x++) {
            for (int y = 0; y < array[x].length; y++) {
                extendArray[x][y] = array[x][y];
            }
        }
        return extendArray;
    }

    /**
     * 将二维数组转化为稀疏数组
     *
     * @return 稀疏数组
     */
    public int[][] originalArray2SparseArray() {
        // 1.统计originalArray中的有效数据个数,并存储节点信息
        int sum = 0;
        int[][] temp = new int[baseSparseRowSize][BASE_SPARSE_COLUMN_SIZE];
        // 稀疏数组第一个节点表示二维数组大小及有效数字个数
        temp[0][0] = originalArray.length;
        temp[0][1] = originalArray[0].length;
        temp[0][BASE_SPARSE_COLUMN_SIZE - 1] = 0;
        for (int i = 0; i < originalArray.length; i++) {
            for (int j = 0; j < originalArray[i].length; j++) {
                // 2.找出有效值,并计数
                if (originalArray[i][j] != INVALID_VALUE) {
                    sum++;
                    temp[0][BASE_SPARSE_COLUMN_SIZE - 1] = sum;
                    // 3.判断是否需要扩容
                    if (temp.length <= sum) {
                        // 扩容temp数组为之前的2倍
                        temp = extendArray(temp, true, false);
                    }
                    temp[sum][0] = i + 1;
                    temp[sum][1] = j + 1;
                    temp[sum][BASE_SPARSE_COLUMN_SIZE - 1] = originalArray[i][j];
                }
            }
        }
        return tempArray2EffectiveArray(temp, sum + 1);
    }


    /**
     * 稀疏数组转化为原始二维数组
     *
     * @return 原始二维数组
     */
    public int[][] sparseArray2OriginalArray() {
        // 1.创建二维数组
        int[][] originalArray = new int[this.sparseArray[0][0]][this.sparseArray[0][1]];
        // 2.补充有效内容
        for (int i = 1; i < sparseArray.length; i++) {
            // 稀疏数组中保存的是列数,从1开始;需要转化为索引
            originalArray[sparseArray[i][0] - 1][sparseArray[i][1] - 1] = sparseArray[i][2];
        }
        return originalArray;
    }


    /**
     * 将临时数组转化为有效数组,即将因扩容导致的行删除
     * @param temp 临时数组
     * @param effectiveRowSize 有效行大小
     * @return 有效数组
     */
    public int[][] tempArray2EffectiveArray(int[][] temp, int effectiveRowSize) {
        // 将原稀疏数组数据拷贝至新稀疏数组中
        int[][] sparseArr = new int[effectiveRowSize][temp[0].length];
        for (int i = 0; i < sparseArr.length; i++) {
            for (int j = 0; j < temp[0].length; j++) {
                sparseArr[i][j] = temp[i][j];
            }
        }
        return sparseArr;
    }


    /**
     * 将数组持久化到磁盘中
     * @param array 数组
     * @throws IOException 异常
     */
    public void enduranceArray(int[][] array) throws IOException {
        File file = new File(ENDURANCE_FILE_NAME);
        // 数据以什么格式存储呢?方便我从中恢复
        FileWriter fileWriter = new FileWriter(file);
        // 将稀疏数组转化为文本格式
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array[i].length; j++) {
                if (j != 0) {
                    sb.append(",");
                }
                sb.append(array[i][j]);
            }
            sb.append("\n");
        }
        fileWriter.write(sb.toString());
        fileWriter.flush();
        fileWriter.close();
    }

    /**
     * 从文件中恢复数据到数组
     * @return 数组
     * @throws IOException 异常
     */
    public int[][] recoveryData2Array() throws IOException {
        int[][] temp = null;
        File file = new File(ENDURANCE_FILE_NAME);
        // 恢复
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String line;
        int rowSize = 0;
        while ((line = bufferedReader.readLine()) != null) {
            String[] split = line.split(",");
            if (temp == null) {
                temp = new int[baseSparseRowSize][split.length];
            }
            if (temp.length <= rowSize) {
                temp = extendArray(temp, true, false);
            }
            for (int i = 0; i < split.length; i++) {
                temp[rowSize][i] = Integer.parseInt(split[i]);
            }
            rowSize++;
        }
        fileReader.close();
        bufferedReader.close();
        // 根据rowSize来删除无效的行
        assert temp != null;
        return tempArray2EffectiveArray(temp, rowSize);
    }
}
